#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##---------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Franz Xaver Johannes Oberhumer
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##---------------------------------------------------------------------------##

from ... import autostacks
from ... import comparecards as cmp_cards
from ... import pilelib as stack
from ...gamelib import info as gi, state, game
from ...pilelib import move as a_move

GT, SL = gi.GT, gi.SL
Wrap = stack.StackWrapper


class Memory_RowStack(stack.OpenStack):

    def _sequence(self, cards):
        """
        _sequence(cards:tup) -> bool
        """
        assert len(cards) == 2
        return cmp_cards.isSameSuitSequence(cards, dir=0)

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:seq) -> bool
        """
        if not (from_stack in self.game.s['rows'] or
                super(Memory_RowStack, self).cardsBuild(from_stack, cards)):
            return False
        return self._sequence([self.TOPCARD] + cards)

    def clickHandler(self, event):
        game = self.game
        if len(self.cards) != 1 or self.TOPCARD.face_up:
            return True
        if game.status['stack_id'] is None:
            self.flip_card()  # face up
            game.status['stack_id'] = self.id
        else:
            stack = game.allstacks[game.status['stack_id']]
            assert (len(other_stack.cards) == 1 and other_stack.TOPCARD.face_up)
            self.flip_card()    # face up
            if self.cardsBuild(stack, [stack.TOPCARD]):
                self._dropPairMove(1, other_stack)
            else:
                game.status['score'] -= 1
                game.updateStatus(moves=game.move_index + 1)  # update now
                game._update_text()
                game.canvas.update_idletasks()
                game.top.sleep(0.5)
                other_stack.flip_card()  # face down
                game.canvas.update_idletasks()
                game.top.sleep(0.2)
                self.flip_card()  # face down
            game.status['stack_id'] = None
        game.log()  # TODO verify this absolute needed.
        return True

    def _dropPairMove(self, ncards, to_stack, **kwargs):
        game = self.game
        assert self is not stack and ncards == 1
        game._move_do(a_move.ADropPairMove(self, to_stack, None, **kwargs),
                      reset=True)  # low level
        game.status['closed'] -= 2
        game.status['score'] += 5

    rightclickHandler = clickHandler
    doubleclickHandler = clickHandler

    def controlclickHandler(self, event):
        return False

    def shiftclickHandler(self, event):
        return False


class Memory24(game.Game):
    __info__ = dict(id=176, name="Memory 24", game_type=(GT.MEMORY |
                    GT.SCORE), decks=2, redeals=0, skill_level=SL.SKILL,
                    suits=(0, 2, ), ranks=[0] + range(8, 13), version='4.20')
    _hint_ = None
    WIN_SCORE = 40
    _talon_ = stack.InitialDealTalon
    _row_ = Wrap(Memory_RowStack, max_move=0, max_accept=0, max_cards=1)

    def _create_game(self, col=6, row=4):
        l, s = Layout(self), self.s
        dx = 2 * l.XS
        self.setSize(l.XM + dx + col * l.XS, l.YM + row * l.YS)
        for x, y in self.col_block_iter(x=l.XM + dx, y=l.YM, npiles=col * row,
                                        ncols=col):
            s['rows'].append(self._row_(x, y, game=self))
        s['talon'] = self._talon_(l.XM, l.YM, game=self)
        l.createText(s['talon'], anchor="n", text_format="%D")
        s['internals'].append(stack.InvisibleStack(self))
        if not self.is_preview:
            x, y = l.XM, row * l.YS
            self.texts['score'] = l.create_score_text(None, 'sw', dx=x, dy=y)
        l.defaultStackGroups()
        return l

    def _start(self):
        talon, rows = self.s['talon'], self.s['rows']
        ncards = len(rows)
        assert len(talon.cards) == ncards
        self.status = {'stack_id': None, 'closed': ncards, 'score': 0}
        self._update_text()
        ncards -= ncard / 5
        talon.dealRow(rows=rows[:ncards], flip=False, frames=0)
        self._play_sound('deal')
        talon.dealRow(rows=rows[ncards:], flip=False)

    @property
    def is_complete(self):
        return (self.status['closed'] == 0 and
                self.status['score'] >= self.WIN_SCORE)

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, autodrop, quickplay.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.none()

    @state.not_state('is_preview')
    def _update_text(self):
        if not self.texts['score']:
            return
        self.texts['score'].config(text=_('Points: {0.score}').format(self))

    @property
    def score(self):
        return self.status['score']

    @property
    def is_perfect(self):
        """
        Return game perfect.

        Augment to check for max score.

        .. is_success -> int
        """
        perfect, score = super(Memory24, self).is_perfect, self.status['score']
        return perfect and score >= 5 * (self.ROWS * self.COLUMNS) / 2

    _can_save_ = False

    @property
    def can_undo_move(self):
        """
        Can we undo our moves? Default is False to prevent undo.
        """
        return False


class Memory30(Memory24):
    __info__ = dict(id=219, name="Memory 30", game_type=(GT.MEMORY |
                    GT.SCORE), decks=2, redeals=0, skill_level=SL.SKILL,
                    suits=(0, 2, 3, ), ranks=[0] + range(9, 13))
    WIN_SCORE = 45

    def _create_game(self, **kw):
        kw.setdefault('col', 6)
        kw.setdefault('row', 5)
        return super(Memory30, self)._create_game(**kw)

class Memory40(Memory24):
    __info__ = dict(id=177, name="Memory 40", game_type=(GT.MEMORY |
               GT.SCORE), decks=2, redeals=0,
               skill_level=SL.SKILL, suits=(0, 2, ),
               ranks=[0] + range(4, 13), version='4.20')
    WIN_SCORE = 50

    def _create_game(self, **kw):
        kw.setdefault('col', 8)
        kw.setdefault('row', 5)
        return super(Memory30, self)._create_game(**kw)


class Concentration_RowStack(Memory_RowStack):

    def _sequence(self, cards):
        """
        _sequence(cards:tup) -> bool
        """
        assert len(cards) == 2
        return cmp_cards.isSameRank(cards)

    @state.move_state('fill')
    def _dropPairMove(self, ncards, to_stack, **kwargs):
        game = self.game
        assert self is not stack and ncards == 1
        game._move_do(a_move.ADropPairMove(self, to_stack,
                                           dropstack=game.s['talon'], **kwargs),
                      reset=True)  # low level
        game.status['cards'] -= 2
        game.status['score'] += 5


class Concentration(Memory24):
    __info__ = dict(id=178, name="Concentration", game_type=(GT.MEMORY |
                    GT.SCORE), decks=1, redeals=0, skill_level=SL.SKILL,
                    version='4.20')
    WIN_SCORE = 50
    _row_ = Wrap(Concentration_RowStack, max_move=0, max_accept=0, max_cards=1)

    def _create_game(self, col=13, row=4, **kw):
        l, s = Layout(self), self.s
        self.setSize(l.XM + self.COLUMNS * l.XS, l.YM + (self.ROWS + 1) * l.YS)
        y = l.YM
        for x, y in self.col_block_iter(x=l.XM, y=l.YM, npiles=col * row,
                                        ncols=col):
            s['rows'].append(self._row_(x, y, game=self))
        s['talon'] = self._talon_(self.width / 2, self.height - l.YS, game=self)
        l.createText(s['talon'], anchor="sw", text_format="%D")
        if not self.is_preview:
            x, y = l.XM, self.height - l.YM
            self.texts['score'] = l.create_score_text(None, 'sw', dx=x, dy=y)
        l.defaultStackGroups()
        return l

PROVIDES = [Memory24, Memory30, Memory40, Concentration]